
%{
#include <FlexLexer.h>
#include <iostream>
#include <compilation/SyntaxTree.h>

#if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-compare"
#endif

#ifndef NDEBUG
  extern bool compilation::display_info_during_parsing;
# define DEBUG_PARSING_SIMPLEC \
  if (compilation::display_info_during_parsing) \
  std::cout
#else
namespace compilation
{
  static constexpr bool display_info_during_parsing = false;
} // compilation
#define DEBUG_PARSING_SIMPLEC \
  if (compilation::display_info_during_parsing) \
  std::cout
#endif // NDEBUG

int mylineno = 0;

typedef union YYSTYPE
{
  std::string* identifier;
  compilation::VirtualType* type;
  compilation::VirtualExpression* expression;
  compilation::VirtualInstruction* instruction;
  compilation::Function* function;
} YYSTYPE;

extern YYSTYPE yylval;
extern compilation::Program::ParseContext parseContext;

enum TokenType
{
  TEOF, TType=258, TIf, TElse, TReturn, TDo, TWhile, TOpenParen, TCloseParen, TOpenBrace,
  TCloseBrace, TOpenBracket, TCloseBracket, TComma, TSemicolon, TStar, TAnd, TColon, TPlus,
  TMinus, TDivide, TLessOrEqual, TGreaterOrEqual, TEqual, TLess, TGreater, TAssign,
  TExpression, TIdentifier, TFunctionExpression
};

inline int getTokenIdentifier(const char* szText)
{
  int uLocal = 0;
  compilation::Scope sScope;
  compilation::Scope::FindResult result =
      parseContext.scope().find(std::string(szText), uLocal, sScope);

  if (result == compilation::Scope::FRLocal)
  {
    yylval.expression =
        new compilation::LocalVariableExpression(
          std::string(szText), uLocal, sScope);
    return TExpression;
  }
  else
  {
    if (parseContext.m_function && (*parseContext.m_function).findParameters(std::string(szText), uLocal))
    {
      yylval.expression =
          new compilation::ParameterExpression(
            std::string(szText), uLocal);
      return TExpression;
    };
    if (result == compilation::Scope::FRGlobal)
    {
      yylval.expression =
          new compilation::GlobalVariableExpression(
            std::string(szText), uLocal);
      return TExpression;
    };
  }

  if (parseContext.functions().find(compilation::Function(std::string(szText))) != parseContext.functions().end())
  {
    yylval.expression = new compilation::FunctionCallExpression(
        const_cast<compilation::Function&>(
            *parseContext.functions().find(
              compilation::Function(std::string(szText)))));
    return TFunctionExpression;
  }

  yylval.identifier = new std::string(szText);
  return TIdentifier;
}

%}

string  \"[^\n"]+\"

ws      [ \t]+

alpha   [A-Za-z]
dig     [0-9]
name    (alpha)({alpha}|{dig}|_)*
num1    {dig}+\.?([eE][-+]?{dig}+)?
num2    {dig}*\.{dig}+([eE][-+]?{dig}+)?
number  {num1}|{num2}

%%

{ws}    /* skip blanks and tabs */

"/*" {
  int c;

  while((c = yyinput()) != 0)
  {
    /**/ if (c == '\n') ++mylineno;
    else if (c == '*')
    {
      if ((c = yyinput()) == '/') break;
      else unput(c);
    }
  }
}

void {
  DEBUG_PARSING_SIMPLEC
    << "type " << yytext << '\n';

  yylval.type = nullptr;
  return TType;
}

int {
  DEBUG_PARSING_SIMPLEC
    << "type " << yytext << '\n';

  yylval.type =
      new compilation::BaseType(compilation::BaseType::VInt);
  return TType;
}

char {
  DEBUG_PARSING_SIMPLEC
    << "type " << yytext << '\n';

  yylval.type =
      new compilation::BaseType(compilation::BaseType::VChar);
  return TType;
}

if        DEBUG_PARSING_SIMPLEC << "keyword " << yytext << '\n'; return TIf;
else      DEBUG_PARSING_SIMPLEC << "keyword " << yytext << '\n'; return TElse;
return    DEBUG_PARSING_SIMPLEC << "keyword " << yytext << '\n'; return TReturn;
do        DEBUG_PARSING_SIMPLEC << "keyword " << yytext << '\n'; return TDo;
while     DEBUG_PARSING_SIMPLEC << "keyword " << yytext << '\n'; return TWhile;
"("       DEBUG_PARSING_SIMPLEC << "open_paren" << '\n'; return TOpenParen;
")"       DEBUG_PARSING_SIMPLEC << "close_paren" << '\n'; return TCloseParen;
"{"       DEBUG_PARSING_SIMPLEC << "open_brace" << '\n'; return TOpenBrace;
"}"       DEBUG_PARSING_SIMPLEC << "close_brace" << '\n'; return TCloseBrace;
"["       DEBUG_PARSING_SIMPLEC << "open_bracket" << '\n'; return TOpenBracket;
"]"       DEBUG_PARSING_SIMPLEC << "close_bracket" << '\n'; return TCloseBracket;
","       DEBUG_PARSING_SIMPLEC << "comma" << '\n'; return TComma;
";"       DEBUG_PARSING_SIMPLEC << "semicolon" << '\n'; return TSemicolon;
"*"       DEBUG_PARSING_SIMPLEC << "star" << '\n'; return TStar;
":"       DEBUG_PARSING_SIMPLEC << "colon" << '\n'; return TColon;
"+"       DEBUG_PARSING_SIMPLEC << "plus" << '\n'; return TPlus;
"-"       DEBUG_PARSING_SIMPLEC << "minus" << '\n'; return TMinus;
"/"       DEBUG_PARSING_SIMPLEC << "divide" << '\n'; return TDivide;
"<="      DEBUG_PARSING_SIMPLEC << "less_or_equal" << '\n'; return TLessOrEqual;
">="      DEBUG_PARSING_SIMPLEC << "greater_or_equal" << '\n'; return TGreaterOrEqual;
"=="      DEBUG_PARSING_SIMPLEC << "equal" << '\n'; return TEqual;
"<"       DEBUG_PARSING_SIMPLEC << "less" << '\n'; return TLess;
">"       DEBUG_PARSING_SIMPLEC << "greater" << '\n'; return TGreater;
"="       DEBUG_PARSING_SIMPLEC << "assign" << '\n'; return TAssign;

{dig}+\.?([eE][-+]?{dig}+)?|{dig}*\.{dig}+([eE][-+]?{dig}+)? {
  DEBUG_PARSING_SIMPLEC
    << "number " << yytext << '\n';

  yylval.expression =
      new compilation::ConstantExpression(atoi(yytext));
  return TExpression;
}

\n mylineno++;

([A-Za-z])([A-Za-z]|[0-9]|_)* {
  DEBUG_PARSING_SIMPLEC
    << "name " << yytext << '\n';

  return getTokenIdentifier(yytext);
}

\"[^\n"]+\" {
  DEBUG_PARSING_SIMPLEC
    << "string " << yytext << '\n';

  yylval.expression =
      new compilation::StringExpression(std::string(yytext));
  return TExpression;
}

%%
